#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# @author: Florian Dorn <florian.dorn@gmail.com>
# @package: gnome-agenda
# @license: GNU GPLv2
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import sys
import copy
try:

#	import pygtk
	import pango
	import gtk
	import gtk.glade
	
	v = gtk.pygtk_version
	if( not (v[0] >1 and v[1] > 9)):
		print "Could not import GTK version (> 2.10)"
		sys.exit(1)		
	
except:
	print "Could not import GTK version (> 2.10)"
	sys.exit(1)


try:
	import re
	import os
	import time
	import string
	import gobject
	from gettext import gettext as _
	from configobj import ConfigObj
except:
	print "python ConfigObj is not installed" 
	sys.exit(1)
	

from pkg_resources import Requirement, resource_filename
import datetime
from optparse import OptionParser

try:
	import sexy
	has_sexy = True
except ImportError:
	has_sexy = False
try:
	import pynotify
except:
	pass
from gnomeagenda.pluginsystem import *
from gnomeagenda.ui.AgendaWindow import AgendaWindow
from gnomeagenda.ui.EditEventWindow import EditEventWindow
#crashes on windows
#from gnomeagenda.ui.AgendaApplet import AgendaApplet

from gnomeagenda.ui.AgendaListView import  AgendaListView

from gnomeagenda.appointment import *
from gnomeagenda import APP_VERSION, APP_NAME

__author__ = 'florian.dorn@gmail.com (Florian Dorn)'
DEBUG = 0


# Parse commandline options
usage = "gnome-agenda [OPTIONS]"
parser = OptionParser(usage=usage)
#parser.add_option("-w", "--window", dest="window", action="store_true", help="Launch the applet in a standalone window for test purposes (default=no)")
parser.add_option("-t", "--tray", dest="tray", action="store_true", help="Run Agneda as a tray icon")
parser.add_option("-v", "--version", dest="version", action="store_true", help="Print version")
#parser.add_option("--oaf-activate-iid")
#parser.add_option("--oaf-ior-fd")
(options, args) = parser.parse_args()


#sys.modules['agenda'] = __import__(__name__)

GLADEFILE = resource_filename("gnomeagenda","gnome-agenda.glade")

class preferencesDialog:
	def __init__(self):
		pass
	def on_activate_provider(self,widget = None, provider = None,cals = []): 
		try:
			page = self.wTree.get_widget("%s_page" %(provider.__module__))
			
			if(widget.get_active()):
				page.show()
			else:
				page.hide()				
		except Exception,e:
			pass
		pass
		
	def on_calendar_toggled_cb(self,cell, path, user_data):
		"""Callback for activating/deavtivating a calendar""" 
		model, column = user_data
		model[path][column] = not model[path][column]
		calendar_id = model[path][0]
		database.setCalendarHidden(calendar_id,model[path][column])
		return
		
	def mk_calendar_treeview(self,provider):
		sql = "select * from Calendar c where provider = '%s' order by lower(name)" % provider
		
		calendars = database.query(sql)
		#liststore: ID, visible, name
		liststore = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_BOOLEAN,gobject.TYPE_STRING)
		treeview = gtk.TreeView(liststore)
		treeview.set_headers_visible(False)
		
		bool_cell = gtk.CellRendererToggle()
		bool_cell.set_property("activatable", True)

		bcolumn = gtk.TreeViewColumn('')
		bcolumn.pack_start(bool_cell)
		bcolumn.add_attribute(bool_cell,'active',1)
		bool_cell.connect("toggled", self.on_calendar_toggled_cb, (liststore, 1))
		
		
		name_col = gtk.TreeViewColumn('')
		
		treeview.append_column(bcolumn)
		treeview.append_column(name_col)
		name_cell = gtk.CellRendererText()
		

		name_col.pack_start(name_cell)
		name_col.add_attribute(name_cell,'markup',2)

		for row in calendars:
			#TODO: get the visible flag from the calendar
			active = True
			if (row[6] != 0):
				active = False
			liststore.append([row[0],active,row[1]])
		return treeview
		
	def run(self):
		self.wTree = gtk.glade.XML(GLADEFILE)
		self.dlg = self.wTree.get_widget("preferencedialog")
		#dynamically build the list of active sources
		
		providers = get_by_capability('provider')
		active_providers = []
		active_providers = database.get('active_providers')
		
		#Populate the active calendar tree
		active_calendar_box = self.wTree.get_widget("active_calendar_box")

		sql = "select distinct provider from Calendar"
		active_calendar_sources = []
		for provider in providers:
			
			l = gtk.Label()
			l.set_markup('<b>%s</b>' % provider.Plugin_Name)
			l.set_alignment(0,0.5)
			
			checkbox = gtk.CheckButton()
			checkbox.add(l)

			active = provider.__module__ in active_providers
			
			checkbox.connect("toggled",self.on_activate_provider,provider)
			checkbox.set_active(active)	
			checkbox.emit("toggled")
			checkbox.set_data('module',"%s" % provider.__module__)
			
			active_calendar_box.pack_start(checkbox,False,False)
			treeview = self.mk_calendar_treeview(provider.__module__)
			active_calendar_box.pack_start(treeview,False,False)
			
			active_calendar_sources.append(checkbox)
			
		active_calendar_box.show_all()
		
		
		pref_refresh = self.wTree.get_widget("pref_refresh")
		pref_reminder = self.wTree.get_widget("pref_reminder")	
		pref_width = self.wTree.get_widget("pref_width")
		pref_height = self.wTree.get_widget("pref_height")
	
		pref_days = self.wTree.get_widget("pref_days")
			
		#google-calendar
		try:
			pref_gcal_username = self.wTree.get_widget("pref_gcal_username")
			pref_gcal_password = self.wTree.get_widget("pref_gcal_password")
			pref_gcal_username.set_text(database.settings['plugins']['GoogleCalendar']['username'])
			pref_gcal_password.set_text(database.settings['plugins']['GoogleCalendar']['password'])
		except:
			pass
		#groupwise
		try:
			pref_gw_server			 = self.wTree.get_widget("pref_gw_server")
			pref_gw_username		   = self.wTree.get_widget("pref_gw_username")
			pref_gw_password		   = self.wTree.get_widget("pref_gw_password")
			pref_gw_connection_ssl  = self.wTree.get_widget("pref_gw_connection_ssl")
			pref_gw_connection_normal	   = self.wTree.get_widget("pref_gw_connection_normal")
			pref_gw_port_normal	 = self.wTree.get_widget("pref_gw_port_normal")
			pref_gw_port_ssl		   = self.wTree.get_widget("pref_gw_port_ssl")
			pref_gw_folder			 = self.wTree.get_widget("pref_gw_folder")
			pref_gw_tzadjust		   = self.wTree.get_widget("pref_gw_tzadjust")

			pref_gw_server.set_text(database.settings['plugins']['Groupwise']['server'])
			pref_gw_username.set_text(database.settings['plugins']['Groupwise']['username'])
			pref_gw_password.set_text(database.settings['plugins']['Groupwise']['password'])
			if(bool(database.settings['plugins']['Groupwise']['ssl'])):
				pref_gw_connection_ssl.set_active(True)
			else:
				pref_gw_connection_normal.set_active(True)
			pref_gw_port_normal.set_value(float(database.settings['plugins']['Groupwise']['port_normal']))
			pref_gw_port_ssl.set_value(float(database.settings['plugins']['Groupwise']['port_ssl']))
			pref_gw_folder.set_text(database.settings['plugins']['Groupwise']['folder'])
			pref_gw_tzadjust.set_value(float(database.settings['plugins']['Groupwise']['timezoneadjust']))
		except:
			pass
		
		pref_refresh.set_value(float(database.settings['refresh']))
		pref_reminder.set_value(float(database.settings['reminder']))

		
		pref_height.set_value(float(database.settings['height']))
		pref_width.set_value(float(database.settings['width']))
		
		pref_days.set_value(float(database.settings['num_days']))
		#get_active()
		#process the settings
		result = self.dlg.run()
		settings = {}
		if(result):
			settings['active_providers'] = []
			for prov_checkbox in active_calendar_sources:
				if(prov_checkbox.get_active()):
					settings['active_providers'].append(prov_checkbox.get_data('module'))
			database.set('active_providers',settings['active_providers'])
			#google-calendar
			try:
				database.settings['plugins']['GoogleCalendar']['username'] = pref_gcal_username.get_text()
				database.settings['plugins']['GoogleCalendar']['password'] = pref_gcal_password.get_text()
			except:
				pass
			#groupwise
			try:
				database.settings['plugins']['Groupwise']['server']   =pref_gw_server.get_text()
				database.settings['plugins']['Groupwise']['username'] =pref_gw_username.get_text()
				database.settings['plugins']['Groupwise']['password'] =pref_gw_password.get_text()
				database.settings['plugins']['Groupwise']['ssl']  = pref_gw_connection_ssl.get_active()
				database.settings['plugins']['Groupwise']['port_normal']= pref_gw_port_normal.get_text()
				database.settings['plugins']['Groupwise']['port_ssl'] = pref_gw_port_ssl.get_text()
				database.settings['plugins']['Groupwise']['folder'] = pref_gw_folder.get_text()				 
				database.settings['plugins']['Groupwise']['timezoneadjust'] = pref_gw_tzadjust.get_text()					   
			except:
				pass
			database.settings['refresh'] = pref_refresh.get_text()
			database.settings['reminder'] = pref_reminder.get_text()
			
			database.settings['width'] = pref_width.get_text()
			database.settings['height'] = pref_height.get_text()
			database.settings['num_days'] = pref_days.get_text()
			
			database.settings.write()
		self.dlg.destroy()
		##reinitialize the providers (de)activate, load config
		database.initializeProviders()
		return result
		
		
class agenda(gobject.GObject):

	def about_icon_cb(self,widget):
		"""
		About clicked
		"""
		
		self.aboutdialog.run()
		self.aboutdialog.hide()
		
		pass
		
		
	def settings_activate_cb(self,widget):
		"""
		Show the preference dialog
		"""
		
		prefs = preferencesDialog()
		result = prefs.run()
		self.window.resize(max(0,int(database.get('width'))),max(0,int(database.get('height'))))
		self.updateView()
		
		pass
		
		

	def popup_menu_cb(self,widget, button = None, time= None, data = None):
		"""right click on the status icon"""

		if(self.statusIcon.get_geometry()):
			self.popup_menu.popup(None, None, gtk.status_icon_position_menu, button, time,self.statusIcon)
		else:
			self.popup_menu.popup(None, None, None, button, time,self.statusIcon)

	def status_icon_click_cb(self,widget=None,menu=None):
		"""left click on the status icon"""
		self.window.show_window()
	
	def menu_position_func(self,menu,data):
		event = data[0]
		widget = data[1]
		x,y = int(event.x_root+1), int(event.y_root+1)

		return (x,y,True)
	def on_configure(self,widget, event):
		print event
		
	def main_button_press_event_cb(self, widget=None,event = None,data = None):

		if ((data == None and event.button == 3) or (data != None and event.button == 1)):
			self.popup_menu.popup(None, None, self.menu_position_func, event.button, event.time,(event,data))

		return True
		pass
	def main_destroy_event_cb(self,widget, data=None):
		##do not call quit() since, the window will be destroyed, and the size/position is not valid anymore
		gtk.main_quit()
	def searchBox_clear(self,widget,filterEntry):
		
		
		if(filterEntry.get_text() != ""):
			filterEntry.set_text("")
			self.search_entry_activate_cb(None,filterEntry)
	def search_entry_activate_cb(self,widget,filterEntry):
		"""
		Perform the search if "ENTER" was hit
		"""
		self.SearchTerm = filterEntry.get_text()
		self.updateView()
		
	def exit_activate_cb(self,widget,data = None):
		"""
		popup_menu_exit handler
		"""
		if(self.statusIcon):
			self.statusIcon.set_visible(False)
		self.saveSettings()
		gtk.main_quit()
	def saveSettings(self):
		pass
		database.set('width',max(0,int(self.window.get_size()[0])))
		database.set('height',max(0,int(self.window.get_size()[1])))
		database.set('hide_finished',bool(self.hide_finished))
	
	def main_delete_event_cb(self,event,data):
		"""delete event of the main window"""
		"""just hide, and stop propagating the event"""
		self.status_icon_click_cb()
		return True
	def dismiss_error_button_clicked_cb(self,widget):
		self.dismissErrors()
	def dismissErrors(self):
		self.errors_label.set_text("")
		database.clearErrors()
		self.errorPanel.hide()
	def updateView(self):
		print "update View"
		resultRows = database.search(self.SearchTerm)
		
		nextUpdateTime = self.calendar.updateView(resultRows)
		now = datetime.datetime.now()
		nr =  (nextUpdateTime - now)
		nextdraw_sec = nr.days * 86400 + nr.seconds + 1
		#print "Next redraw at %s (%s sec)" % (nextUpdateTime,nextdraw_sec)
		self.timeout_redraw = gobject.timeout_add(nextdraw_sec * 1000, self.updateView)
		
		
		pass
	def getErrors(self):
		error_text = []
		for error in database.getErrors():
			try:
				name = error[0]
			except:
				name = ""
			try:
				prov_errors = ("\n".join(error[1]))
			except:
				prov_errors = ""
			error_text.append("<b><big>%s</big></b>\n%s\n" %(name,prov_errors))
		return ("\n".join(error_text)).strip()
	def updateErrors(self):
		errors = self.getErrors()
		self.errors_label.set_markup(errors)
		if(len(errors) > 0):
			self.errorPanel.show()
		else:
			self.errorPanel.hide()
	def setBusy(self, busy = True):
		if(busy):
			cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)				  
		else:
			cursor = None
		try:
			gdkwin = self.window.window
			if(gdkwin):
				gdkwin.set_cursor(cursor)
		except Exception,e:
			pass
		gtk.gdk.flush()
	def refresh_cb(self,widget=None):
		""" refresh """
		self.setBusy(True)
		database.updateCalendars()
		self.setBusy(False)
		self.updateErrors()
		self.updateView()
		pass
	def widget_initialized(self):
		"""
		Called at the end of init
		"""
		self.updateView()
		self.window.resize(max(0,int(database.get('width'))),max(0,int(database.get('height'))))

		screen_w, screen_h = gtk.gdk.screen_width(), gtk.gdk.screen_height()
		self.hide_finished = bool(database.get('hide_finished'))
		try:
			self.pynotify_ok = pynotify.init(APP_NAME)
		except:
			self.pynotify_ok = False
		if(self.pynotify_ok and int(database.settings['reminder']) > 0):
			gobject.timeout_add(10000, self.remind)	
	def notify(self,event):
		pass
	def remind(self,):
		"""This has to be reimplemented"""
		return
		sql = "SELECT * from Events e, Calendar c WHERE c.hidden = 0 and c.id = e.calendar_id and datetime(e.start_time) > datetime('now') and datetime(e.start_time) < datetime('now','%s minutes') order by e.start_time"
		sql = sql % database.settings['reminder']
		toremind = database.query(sql)
		for row in toremind:
			id = row[0]
			try:
				self.notified_ids.index(id)
				exists = True
			except:
				exists = False
			if(not exists):
				n = pynotify.Notification(row[5],self.getToolipText(row))
				n.set_icon_from_pixbuf(self.app_icon)
				n.set_urgency(pynotify.URGENCY_CRITICAL)
				n.set_timeout(pynotify.EXPIRES_NEVER)
				n.show()
				self.notified_ids.append(id)
		self.timeout_alert = gobject.timeout_add(10000, self.remind)
		return False

	def sexy_searchbox_pressed(self,filterEntry,pos,button, data = None):
		if(pos == sexy.ICON_ENTRY_SECONDARY):
			if(filterEntry.get_text() != ""):
				filterEntry.set_text("")
				self.search_entry_activate_cb(None,filterEntry)	
		elif (pos == sexy.ICON_ENTRY_PRIMARY):
			#TODO: popup the filter changer
			print "sexy.ICON_ENTRY_PRIMARY"
			
			pass
	def on_filterEntry_key_pressed(self,  filterEntry, data):
		keyname = gtk.gdk.keyval_name(data.keyval)
		if keyname == "Escape":
			filterEntry.set_text("")
			self.search_entry_activate_cb(None,filterEntry)	

	def addEventBtn_activate_cb(self,t):
		print t
		print "addEventBtn_activate_cb"
		newEvtDlg = EditEventWindow(self.window,None)
		response = newEvtDlg.run()
	def __init__(self):
		"""window is hidden on startup"""
		#The last searched Term
		self.SearchTerm = ""
		#hide finished events
		self.hide_finished = True
		#hold the timeout-ids for refreshing/alerting
		self.timeout_refresh = -1
		self.timeout_alert = -1
		self.timeout_redraw = -1
		#stores event-ids that ahe already been notified
		self.notified_ids = []
		#Set the Glade file

		self.wTree = gtk.glade.XML(GLADEFILE)
		

		pixbuf = load_stock_image("x-office-calendar")
		
		gtk.window_set_default_icon(pixbuf)
		self.statusIcon = gtk.StatusIcon()
		self.statusIcon.set_from_pixbuf(pixbuf)
		self.statusIcon.set_tooltip(APP_NAME)
		

		
		self.statusIcon.connect('popup-menu', self.popup_menu_cb)
		self.statusIcon.connect('activate', self.status_icon_click_cb)
		self.statusIcon.set_visible(True)
		
		panel = self.wTree.get_widget("search_container")
		
		find_image = gtk.Image()
		find_image.set_from_stock(gtk.STOCK_FIND,gtk.ICON_SIZE_MENU)
			
		if has_sexy:
			filterEntry = sexy.IconEntry()
			filterEntry.add_clear_button()
			filterEntry.connect('icon-pressed',self.sexy_searchbox_pressed)
			filterEntry.set_icon (sexy.ICON_ENTRY_PRIMARY, find_image)
			#filterEntry.set_icon_highlight (sexy.ICON_ENTRY_PRIMARY,True)


			panel.pack_start(filterEntry)
			panel.reorder_child(filterEntry,1)
			filterEntry.show()
		else:
			hbox = gtk.HBox()

			filterChangeBtn = gtk.Button()
			
			filterEntry = gtk.Entry()
			clear_button = gtk.Button()
			i = gtk.Image()
			i.set_from_stock(gtk.STOCK_CLEAR,gtk.ICON_SIZE_MENU)

			#filterChangeBtn.add(find_image)
			#filterChangeBtn.set_focus_on_click(False)
			#filterChangeBtn.set_relief(gtk.RELIEF_NONE)
			#filterChangeBtn.set_property("can-focus",False)
			clear_button.add(i)
			clear_button.set_focus_on_click(False)
			clear_button.set_relief(gtk.RELIEF_NONE)
			clear_button.connect('clicked',self.searchBox_clear,filterEntry)
			clear_button.set_property("can-focus",False)
			#hbox.pack_start(filterChangeBtn,False)
			hbox.pack_start(filterEntry)
			hbox.pack_start(clear_button,False)
			
			panel.pack_start(hbox)
			panel.reorder_child(hbox,1)
			
			hbox.set_border_width(2)
			
			filterEntry.show()
			clear_button.show()


		filterEntry.connect('activate',self.search_entry_activate_cb,filterEntry)
		filterEntry.connect("key-press-event", self.on_filterEntry_key_pressed)

		
		
		
		self.window = AgendaWindow(self.statusIcon)
		
		#frame = self.wTree.get_widget("gnome-agenda-frame")
		#frame.modify_bg(frame.state,frame.style.white)

		#frame.reparent(self.window)
		#frame.set_border_width(0)
		
		panel = self.wTree.get_widget("panel")
		panel.modify_bg(panel.state,panel.style.white)

		panel.reparent(self.window)
		panel.set_border_width(0)
				
		#Connect to the signals
		self.wTree.signal_autoconnect(self)
		self.window.connect("button-press-event",self.main_button_press_event_cb)
		#the global widgets
		self.errorPanel = self.wTree.get_widget("errorPanel")
		self.popup_menu = self.wTree.get_widget("popup_menu")
		self.search_entry = self.wTree.get_widget("search_entry")
		self.errors_label = self.wTree.get_widget("errors_label")
		self.scroll_area =  self.wTree.get_widget("scroll_area")
		self.aboutdialog = self.wTree.get_widget("aboutdialog")
		self.aboutdialog.set_version(APP_VERSION)
		self.aboutdialog.set_program_name(APP_NAME)
		

		self.toggle_window_radio_btn = self.wTree.get_widget("toggle_window_radio_btn")
		self.calendar = AgendaListView()
		
		cal_viewport = self.wTree.get_widget("cal_viewport")
		cal_viewport.add(self.calendar)
						
		#self.scroll_area.get_child().add(self.calendar)
		self.scroll_area.set_size_request(1,-1)
		
		self.tooltips = gtk.Tooltips()
		##add the status icon
		

		self.app_icon = load_stock_image("x-office-calendar",gtk.ICON_SIZE_DIALOG)
		#THIS HAS TO BE THE LAST LINE IN __init__
		self.widget_initialized()



#if options.debug:
#	logging.getLogger().setLevel(logging.DEBUG)

#holds the appointments
database = None
mydir = os.path.dirname( os.path.realpath(__file__) ) or "."
os.chdir(mydir)
#initialize the plugin system first

if options.version:
	print APP_VERSION
	sys.exit()

init_plugin_system()
database = Appointments()

def applet_factory(applet, iid):
#	logging.info ('Starting Deskbar instance: %s %s', applet, iid)
	
	app = AgendaApplet(applet)
	app.show_all()
	
	return True

def build_window():
	app = gtk.Window(gtk.WINDOW_TOPLEVEL)
	# translators: This is the window title.
	app.set_title(APP_NAME)
	app.connect("destroy", gtk.main_quit)
	
	applet = gnomeapplet.Applet()
	applet.get_orient = lambda: gnomeapplet.ORIENT_DOWN
	applet_factory(applet, None)
	applet.reparent(app)
	    
	app.show_all()
	
	return app

def load_stock_image(name='stock_calendar',size = gtk.ICON_SIZE_SMALL_TOOLBAR ):
	dir = gtk.TEXT_DIR_NONE
	state =gtk.STATE_NORMAL
	try:
		pixbuf = gtk.icon_theme_get_default().load_icon(name, gtk.icon_size_lookup(size)[0], gtk.ICON_LOOKUP_USE_BUILTIN)
	except gobject.GError, exc:
		return gtk.icon_theme_get_default().load_icon(gtk.STOCK_MISSING_IMAGE, gtk.icon_size_lookup(size)[0], gtk.ICON_LOOKUP_USE_BUILTIN)
	return pixbuf



#for now force to the tray	
options.tray = True
# Enable threads
gtk.gdk.threads_init()


#try:
#	import gnomeapplet
#	import gnome
#except:
#	options.tray = True

if options.tray:
	agenda = agenda()
	gtk.gdk.threads_enter()
	gtk.main()
	gtk.gdk.threads_leave()
#elif options.window:
#	import gnome

	#gnome.init(deskbar.defs.PACKAGE, deskbar.defs.VERSION)
#	build_window()
#	gtk.gdk.threads_enter()
#	gtk.main()
#	gtk.gdk.threads_leave()
#else:
#	
#	gnomeapplet.bonobo_factory(
#			"OAFIID:Deskbar_Applet_Factory",
#			AgendaApplet.__gtype__,
#			"gnome-agenda",
#			APP_VERSION,
#			applet_factory)



#if __name__ == "__main__":
#		mydir = os.path.dirname( os.path.realpath(__file__) ) or "."
#		os.chdir( mydir)
#		#initialize the plugin system first
#	init_plugin_system()
#	database = appointment.Appointments()
#	
#	#register stock icons and start the gui


#	

#	gtk.main()
